package net.minecraft.world.storage;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.nbt.CompressedStreamTools;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.MinecraftException;
import net.minecraft.world.WorldProvider;
import net.minecraft.world.chunk.storage.IChunkLoader;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class SaveHandler implements ISaveHandler, IPlayerFileData {
	private static final Logger logger = LogManager.getLogger();

	/** The directory in which to save world data. */
	private final File worldDirectory;

	/** The directory in which to save player data. */
	private final File playersDirectory;
	private final File mapDataDir;

	/**
	 * The time in milliseconds when this field was initialized. Stored in the
	 * session lock file.
	 */
	private final long initializationTime = MinecraftServer.getCurrentTimeMillis();

	/** The directory name of the world */
	private final String saveDirectoryName;

	public SaveHandler(File savesDirectory, String directoryName, boolean playersDirectoryIn) {
		this.worldDirectory = new File(savesDirectory, directoryName);
		this.worldDirectory.mkdirs();
		this.playersDirectory = new File(this.worldDirectory, "playerdata");
		this.mapDataDir = new File(this.worldDirectory, "data");
		this.mapDataDir.mkdirs();
		this.saveDirectoryName = directoryName;

		if (playersDirectoryIn) {
			this.playersDirectory.mkdirs();
		}

		this.setSessionLock();
	}

	/**
	 * Creates a session lock file for this process
	 */
	private void setSessionLock() {
		try {
			File file1 = new File(this.worldDirectory, "session.lock");
			DataOutputStream dataoutputstream = new DataOutputStream(new FileOutputStream(file1));

			try {
				dataoutputstream.writeLong(this.initializationTime);
			} finally {
				dataoutputstream.close();
			}
		} catch (IOException ioexception) {
			ioexception.printStackTrace();
			throw new RuntimeException("Failed to check session lock, aborting");
		}
	}

	/**
	 * Gets the File object corresponding to the base directory of this world.
	 */
	public File getWorldDirectory() {
		return this.worldDirectory;
	}

	/**
	 * Checks the session lock to prevent save collisions
	 */
	public void checkSessionLock() throws MinecraftException {
		try {
			File file1 = new File(this.worldDirectory, "session.lock");
			DataInputStream datainputstream = new DataInputStream(new FileInputStream(file1));

			try {
				if (datainputstream.readLong() != this.initializationTime) {
					throw new MinecraftException("The save is being accessed from another location, aborting");
				}
			} finally {
				datainputstream.close();
			}
		} catch (IOException var7) {
			throw new MinecraftException("Failed to check session lock, aborting");
		}
	}

	/**
	 * initializes and returns the chunk loader for the specified world provider
	 */
	public IChunkLoader getChunkLoader(WorldProvider provider) {
		throw new RuntimeException("Old Chunk Storage is no longer supported.");
	}

	/**
	 * Loads and returns the world info
	 */
	public WorldInfo loadWorldInfo() {
		File file1 = new File(this.worldDirectory, "level.dat");

		if (file1.exists()) {
			try {
				NBTTagCompound nbttagcompound2 = CompressedStreamTools.readCompressed(new FileInputStream(file1));
				NBTTagCompound nbttagcompound3 = nbttagcompound2.getCompoundTag("Data");
				return new WorldInfo(nbttagcompound3);
			} catch (Exception exception1) {
				exception1.printStackTrace();
			}
		}

		file1 = new File(this.worldDirectory, "level.dat_old");

		if (file1.exists()) {
			try {
				NBTTagCompound nbttagcompound = CompressedStreamTools.readCompressed(new FileInputStream(file1));
				NBTTagCompound nbttagcompound1 = nbttagcompound.getCompoundTag("Data");
				return new WorldInfo(nbttagcompound1);
			} catch (Exception exception) {
				exception.printStackTrace();
			}
		}

		return null;
	}

	/**
	 * Saves the given World Info with the given NBTTagCompound as the Player.
	 */
	public void saveWorldInfoWithPlayer(WorldInfo worldInformation, NBTTagCompound tagCompound) {
		NBTTagCompound nbttagcompound = worldInformation.cloneNBTCompound(tagCompound);
		NBTTagCompound nbttagcompound1 = new NBTTagCompound();
		nbttagcompound1.setTag("Data", nbttagcompound);

		try {
			File file1 = new File(this.worldDirectory, "level.dat_new");
			File file2 = new File(this.worldDirectory, "level.dat_old");
			File file3 = new File(this.worldDirectory, "level.dat");
			CompressedStreamTools.writeCompressed(nbttagcompound1, new FileOutputStream(file1));

			if (file2.exists()) {
				file2.delete();
			}

			file3.renameTo(file2);

			if (file3.exists()) {
				file3.delete();
			}

			file1.renameTo(file3);

			if (file1.exists()) {
				file1.delete();
			}
		} catch (Exception exception) {
			exception.printStackTrace();
		}
	}

	/**
	 * used to update level.dat from old format to MCRegion format
	 */
	public void saveWorldInfo(WorldInfo worldInformation) {
		NBTTagCompound nbttagcompound = worldInformation.getNBTTagCompound();
		NBTTagCompound nbttagcompound1 = new NBTTagCompound();
		nbttagcompound1.setTag("Data", nbttagcompound);

		try {
			File file1 = new File(this.worldDirectory, "level.dat_new");
			File file2 = new File(this.worldDirectory, "level.dat_old");
			File file3 = new File(this.worldDirectory, "level.dat");
			CompressedStreamTools.writeCompressed(nbttagcompound1, new FileOutputStream(file1));

			if (file2.exists()) {
				file2.delete();
			}

			file3.renameTo(file2);

			if (file3.exists()) {
				file3.delete();
			}

			file1.renameTo(file3);

			if (file1.exists()) {
				file1.delete();
			}
		} catch (Exception exception) {
			exception.printStackTrace();
		}
	}

	/**
	 * Writes the player data to disk from the specified PlayerEntityMP.
	 */
	public void writePlayerData(EntityPlayer player) {
		try {
			NBTTagCompound nbttagcompound = new NBTTagCompound();
			player.writeToNBT(nbttagcompound);
			File file1 = new File(this.playersDirectory, player.getUniqueID().toString() + ".dat.tmp");
			File file2 = new File(this.playersDirectory, player.getUniqueID().toString() + ".dat");
			CompressedStreamTools.writeCompressed(nbttagcompound, new FileOutputStream(file1));

			if (file2.exists()) {
				file2.delete();
			}

			file1.renameTo(file2);
		} catch (Exception var5) {
			logger.warn("Failed to save player data for " + player.getName());
		}
	}

	/**
	 * Reads the player data from disk into the specified PlayerEntityMP.
	 */
	public NBTTagCompound readPlayerData(EntityPlayer player) {
		NBTTagCompound nbttagcompound = null;

		try {
			File file1 = new File(this.playersDirectory, player.getUniqueID().toString() + ".dat");

			if (file1.exists() && file1.isFile()) {
				nbttagcompound = CompressedStreamTools.readCompressed(new FileInputStream(file1));
			}
		} catch (Exception var4) {
			logger.warn("Failed to load player data for " + player.getName());
		}

		if (nbttagcompound != null) {
			player.readFromNBT(nbttagcompound);
		}

		return nbttagcompound;
	}

	public IPlayerFileData getPlayerNBTManager() {
		return this;
	}

	/**
	 * Returns an array of usernames for which player.dat exists for.
	 */
	public String[] getAvailablePlayerDat() {
		String[] astring = this.playersDirectory.list();

		if (astring == null) {
			astring = new String[0];
		}

		for (int i = 0; i < astring.length; ++i) {
			if (astring[i].endsWith(".dat")) {
				astring[i] = astring[i].substring(0, astring[i].length() - 4);
			}
		}

		return astring;
	}

	/**
	 * Called to flush all changes to disk, waiting for them to complete.
	 */
	public void flush() {
	}

	/**
	 * Gets the file location of the given map
	 */
	public File getMapFileFromName(String mapName) {
		return new File(this.mapDataDir, mapName + ".dat");
	}

	/**
	 * Returns the name of the directory where world information is saved.
	 */
	public String getWorldDirectoryName() {
		return this.saveDirectoryName;
	}
}
